package org.chungyan5.mnt.lib;

import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

/**
 * 
 */

/**
 * @author yan
 *
 */
public class Mnt implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -7889982147471011415L;
	
	/**
	 * Using Java Collection Set algorithm: Tags TreeSet does the ordering(ascending), do comparison and cannot have duplicated tags inside this list 
	 */
	private MNTTagComparator mntTagComparator = new MNTTagComparator();
	private TreeSet<MNTTag> selectedPlusTags = new TreeSet<MNTTag>(mntTagComparator);
	private TreeSet<MNTTag> relTags = new TreeSet<MNTTag>(mntTagComparator);
	private TreeSet<MNTTag> selectedNegTags = new TreeSet<MNTTag>(mntTagComparator);
	
	private Item itemForRelTags = null;
	private Set<MNTTag> relTagsForSpecItem;
	
	private Db dbObject;
	  
	/**
	 * 
	 */
	public Mnt(Db dbObject) {
		this.dbObject = dbObject;
	}

	public boolean open() {
		return this.dbObject.open();
	}
	
	public void close() {
		this.dbObject.close();
	}
	
	/**
	 * @param tag tag to be handled
	 * @param select_unsel true as selected tag, otherwise false
	 */
	public void pushSelTag(MNTTag tag, boolean select_unsel) {
		
		if (select_unsel) selectedPlusTags.add(tag);
		else selectedNegTags.add(tag);
		
	}
	
	/**
	 * @param tag tag to be handled
	 * @param select_unsel true as selected tag, otherwise false
	 * @return true as done, otherwise false
	 */
	public boolean removeSelTag(MNTTag tag, boolean select_unsel) {
		
		if (select_unsel) return selectedPlusTags.remove(tag);
		else return selectedNegTags.remove(tag); 
	}
	
	/**
	 * @return the selectedPlusTags
	 */
	public Set<MNTTag> getSelPlusTags() {
		return selectedPlusTags;
	}

	/**
	 * @return the selectedNegTags
	 */
	public Set<MNTTag> getSelNegTags() {
		return selectedNegTags;
	}

	public void clrSearchingSession() {
		selectedPlusTags.clear();
		selectedNegTags.clear();
	}
	
	public MNTTag newTag(MNTTag tag) {
		return dbObject.newTag(tag);
	}
	
	/**
	 * query existing tag(s) in alphabetic
	 * @return List<MNTTag> ordered collection(usually by alphabetical order) of tag(s) 
	 */
	public List<MNTTag> queryTags() {
		return dbObject.queryTags();
	}
	
	/**
	 * query one tag by exactly that id
	 * @param id a known tag id
	 * @return corresponding tag
	 */
	public MNTTag queryTag(long id) {
		return dbObject.queryTag(id);
	}
	
	public MNTTag updateTag(MNTTag tag) {
		return dbObject.updateTag(tag);
	}
	
	public boolean removeTag(MNTTag tag) {
		if (dbObject.removeTag(tag) && dbObject.removeTagFrItemTagRel(tag))
			return true;
		else 
			return false;
	}

	public Item newItem(Item item) {
		item.setEmptyTag(true);
		return dbObject.newItem(item);
	}
	
	public Item updateItem(Item item) {
		return dbObject.updateItem(item);
	}
	
	/**
	 * @param item attached the tag to this item
	 * @param tag using this tag to attach into this item
	 * @param addRemove attached or un-attached
	 * @return this item after attached or un-attached; if null, un-successful
	 */
	public Item itemAttachedTag(Item item, MNTTag tag, boolean addRemove) {
		if (addRemove) {
			item.setEmptyTag(false);
			item = updateItem(item);
			if (dbObject.connectTagItem(item, tag)) return item;
			else return null;
		} else {
			dbObject.disConnectTagItem(item, tag);
			Set<MNTTag> tags = dbObject.queryRelTags(item);
			if (tags.size()==0) {
				item.setEmptyTag(true);
				dbObject.updateItem(item);
			}
			return item;
		}
	}
	
	/**
	 * query one item by exactly that id
	 * @param id a known item id
	 * @return corresponding item
	 */
	public Item queryItem(long id) {
		return dbObject.queryItem(id);
	}
	
	/**
	 * Query items by selected and un-selected tag(s) as filtering; If both of selected and un-selected tag(s) 
	 * 	are null, it will query the item(s) which does/do not included any un-selected and selected tag(s)     
	 * @return be queried item(s) in List<Item> format as ordered collection
	 */
	public List<Item> queryItems() {
		if (selectedPlusTags.size()==0 && selectedNegTags.size()==0) {
			return dbObject.queryItemsWithEmptyTag();
		} else {
			
			// query existing item(s) by selected tag and non-selected Tag through sql ===
			List<Item> items = dbObject.queryItems(
					selectedPlusTags, selectedNegTags);
			
			// clr the previous related tag(s) ===========================================
			relTags.clear();
			
			// list the related tag(s) ===================================================
			//		chk the above corresponding item(s) one by one
			Iterator<Item> itr = items.iterator();
			while (itr.hasNext()) {
				
			//		query related tag(s) of specific item from "Tags and Items many to many relation table"
				Set<MNTTag> relTagsForSpecItem = dbObject.queryRelTags(itr.next());
				
			//		save this related tag(s) if does not add before
				Iterator<MNTTag> itrRelTags = relTagsForSpecItem.iterator();
				while (itrRelTags.hasNext()) {
					MNTTag relTag = itrRelTags.next();
					if (selectedPlusTags.contains(relTag)) {
					} else relTags.add(relTag);
						
				}
				
			//		save the Related Tags List
			//			already in relTags
				
			}
			
			return items;
		}
	}
	
	/**
	 * @return the relTags return related tags, then return non-related tags 
	 */
	public TreeSet<MNTTag> getRelTags() {
		return relTags;
	}

	/**
	 * @param item collecting selected and non-selected tag(s) for this item
	 * @return
	 */
	public Set<MNTTag> getSelTags(Item item) {

		// input a item, if this item does not same to ItemForRelatedTags
		if (itemForRelTags!=item) {
			
		// query related tag(s) of specific item from "Tags and Items many to many relation table"
		// must be a TreeSet<MNTTag>(mntTagComparator) object for tag id comparison 
			relTagsForSpecItem = dbObject.queryRelTags(item);
			
		// save ItemForRelatedTags by this item
			itemForRelTags = item;
		}
		
		// return this chosen list
		return relTagsForSpecItem;
	}

	public Set<MNTTag> getNonSelTags(Item item) {

		Set<MNTTag> nonSelTags = new TreeSet<MNTTag>(mntTagComparator);

		// input a item, if this item does not same to ItemForRelatedTags, call query chosen Tags List from a Item
		if (itemForRelTags!=item) getSelTags(item);
			
		// query existing tag(s)
		List<MNTTag> listTags = queryTags();
		
		// check the tag one by one, if this tag not already in chosen list, save this into non-chosen
		Iterator<MNTTag> itrListTags = listTags.iterator();
		while (itrListTags.hasNext()) {
			MNTTag tag = itrListTags.next();
			if (relTagsForSpecItem.contains(tag)) {
				
			} else nonSelTags.add(tag);
		}
		
		return nonSelTags;
	}
	
	public boolean removeItem(Item item) {
		if (dbObject.removeItem(item) && dbObject.removeFrItemTagRel(item))
			return true;
		else 
			return false;
	}

}
